import copy
import random
import sys

import matplotlib.pyplot as plt
import numpy as np
import xlrd
import math

from sympy import Integer

from question2.NSGA2 import NSGA2

pa = None
od = None
"""区间运行时间"""
onR = None
R = None
L = None
se = None
n_max = 24
n_min = 3
It = 108
t_ba = 0.04
C = 1860

c1 = 10
c2 = 20

result = set()


class Gene(object):

    def __init__(self, m, K, hs, he, z, h, e):
        self.m = m
        self.K = K
        self.hs = hs
        self.he = he
        self.z = z
        self.h = h
        self.e = e
        # """lamda"""
        # self.lam = np.zeros(K, 31)
        # for k in range(1,K+1):
        #     for i in range(1,31):
        #         if k % (m+1) == 0 :
        #             self.lam[k][i] = 1
        #         elif k % (m+1) != 0 and hs <= i <= he:
        #             self.lam[k][i] = 1
        #         else:
        #             self.lam[k][i] = 0
        """出发时间d"""
        self.dt = np.zeros((self.K + 1, 31))
        """到达时间a"""
        self.a = np.zeros((self.K + 1, 31))
        """列车K当前容量"""
        self.cap = np.zeros(self.K + 1)
        for i in range(1, len(self.cap)):
            self.cap[i] = C
        """站点人数"""
        self.people = self.pro_people()
        self.company = None
        self.service = None
        self.yes = False

    def pro_people(self):
        people = np.zeros(31)
        for i in range(1, 30):
            for j in range(i + 1, 31):
                people[i] += od[i][j]
        return people

    def fit(self):
        big_length = 0
        for i in range(1, 30):
            big_length += L[i][i + 1]
        # print("biglength:", big_length)
        small_len = 0
        for i in range(self.hs, self.he):
            small_len += L[i][i + 1]
        self.company = c1 * self.K \
                       + c2 * (big_length * (self.K // (self.m + 1))
                               + small_len * (self.K - (self.K // (self.m + 1))))

    def constraint(self):
        self.yes = self.evolution()
        # print(self.yes)
        dt = self.dt
        a = self.a
        hs = self.hs
        # print(dt)
        # print("................................................")
        # print(a)
        # print("-------------------------------------------------------")
        for k in range(2, self.K + 1):
            for i in range(1, len(dt)):
                if i != 1 and i != hs and a[k][i] != 0 and dt[k][i] != 0:
                    if dt[k][i] < a[k][i]:
                        self.yes = False
                        print(dt[k][i] - dt[k - 1][i] < 108)
                        print(111111111111111111)
                        print(dt[k][i] < a[k][i])
                        # print(dt[k][i])
                        # print(a[k][i])
                        # print(22222222222222222)
                        break
        # print(self.yes)

    def evolution(self):
        t = Integer(0)
        kt = 0
        """服务水平时间"""
        serT = 0

        """"""
        hs = self.hs
        people = self.people
        h = self.h
        m = self.m
        cap = self.cap
        dt = self.dt
        a = self.a
        e = self.e
        """站点的组成成分"""
        d = np.copy(od)
        """列车k的组成成分"""
        value = np.zeros((31, 31))
        while True:
            print("t:", t)
            # print(k)
            """异常检查"""
            for val in cap:
                if val < 0:
                    # print(11)
                    sys.exit(11)
                    return False
            if t == a[self.K][30] and a[self.K][30] != 0:
                print(a[self.K][30])
                sys.exit()
                break
            """"""
            if t % h == 0 and d.any():  # d不是全0:  # 到了发车时间
                kt = kt + 1  # 发一列车
                # print(kt)
                if kt % (m + 1) == 0:  # 大交路 站点1发车
                    dt[kt][1] = t  # k车在1站发车时间
                    a[kt][2] = dt[kt][1] + R[1][2]  # k车在下一站到达时间
                    for j in range(1, 31):  # 上车
                        if d[1][j] <= cap[kt]:
                            cap[kt] -= d[1][j]
                            value[1][j] += d[1][j]
                            d[1][j] = 0
                        else:
                            d[1][j] -= cap[kt]
                            value[1][j] += cap[kt]
                            cap[kt] = 0
                    """ser"""
                    for zj in range(2, 31):
                        serT += value[1][zj] * onR[1][zj]
                        serT += e[zj]
                else:  # 小交路发车
                    dt[kt][hs] = t
                    a[kt][hs + 1] = dt[kt][hs] + R[hs][hs + 1]
                    for j in range(hs + 1, 31):  # 上车
                        if d[hs][j] <= cap[kt]:
                            cap[kt] -= d[hs][j]

                            # print(hs)
                            # print(j)
                            # print(value[hs][j])
                            value[hs][j] += d[hs][j]
                            d[hs][j] = 0
                        else:
                            d[hs][j] -= cap[kt]
                            value[hs][j] += cap[kt]
                            cap[kt] = 0
                    """ser"""
                    for zj in range(hs + 1, 31):
                        serT += value[hs][zj] * onR[hs][zj]
                        serT += e[zj]
            for k in range(1, self.K+1):
                ss = -1
                if k % (m + 1) == 0:
                    ss = 2
                else:
                    ss = hs + 1
                for i in range(ss, 31):
                    # print("a[{}][{}]:{}".format(k, i, a[k][i]))
                    # print("dt[{}][{}]:{}".format(k, i, dt[k][i]))
                    a[k][i] = dt[k][i - 1] + R[i - 1][i]
                    dt[k][i] = a[k][i] + e[i]
                    if t == a[k][i]:  # 列车到i站
                        dt[k][i] = a[k][i] + e[i]  # 在i站开车时间
                        if i != 30:
                            a[k][i + 1] = dt[k][i] + R[i][i + 1]
                        # print(R[i][i+1])
                        # print(k)
                        # print(i)
                        # print(dt[k][i])
                        # print(a[k][i+1])
                        t_down = 0  # 下车时间计时
                        for p in range(1, i):  # 下车
                            t_down += t_ba * value[p][i]
                            # print(value[p][i])
                            cap[k] += value[p][i]
                            value[p][i] = 0
                        if t_down > e[i]:  # 人下不来
                            # print(t_down)
                            if t_down > 120:
                                return False
                            e[i] = random.randint(math.ceil(t_down), 120)
                            dt[k][i] = a[k][i] + e[i]  # 在i站开车时间
                            if i != 30:
                                a[k][i + 1] = dt[k][i] + R[i][i + 1]
                        eta = e[i] - t_down
                        for j in range(i + 1, 31):  # 上车  注意时间约束
                            if d[i][j] <= cap[k] and eta >= d[i][j] * t_ba:
                                cap[k] -= d[i][j]
                                value[i][j] += d[hs][j]
                                """剩余时间约束"""
                                eta -= d[i][j] * t_ba
                                """"""
                                d[i][j] = 0
                                """ser"""
                                if d[k][1] == 0:
                                    serT += (a[k][i] - dt[k][hs]) * d[hs][j]
                                else:
                                    serT += (a[k][i] - dt[k][1]) * d[hs][j]
                                serT += onR[i][j] * d[hs][j]
                                serT += e[j] * d[hs][j]
                            elif d[i][j] <= cap[k] and eta < d[i][j] * t_ba:  # 空间够，时间不够
                                if d[i][j] >= eta // t_ba:
                                    cap[k] -= eta // t_ba  # 时间够上eta // tba 个人
                                    value[i][j] += eta // t_ba
                                    d[i][j] -= eta // t_ba
                                    """剩余时间约束"""
                                    eta -= eta // t_ba * t_ba
                                    """ser"""
                                    delta = eta // t_ba
                                    if d[k][1] == 0:
                                        serT += (a[k][i] - dt[k][hs]) * delta
                                    else:
                                        serT += (a[k][i] - dt[k][1]) * delta
                                    serT += onR[i][j] * delta
                                    serT += e[j] * delta
                                else:
                                    cap[k] -= d[i][j]
                                    value[i][j] += d[i][j]
                                    """剩余时间约束"""
                                    eta -= d[i][j] * t_ba
                                    """"""
                                    d[i][j] = 0
                                    """ser"""
                                    delta = d[i][j]
                                    if d[k][1] == 0:
                                        serT += (a[k][i] - dt[k][hs]) * delta
                                    else:
                                        serT += (a[k][i] - dt[k][1]) * delta
                                    serT += onR[i][j] * delta
                                    serT += e[j] * delta

                            elif d[i][j] > cap[k] and eta >= d[i][j] * t_ba:  # 空间不够，时间够
                                d[i][j] -= cap[k]
                                value[i][j] += cap[k]
                                """剩余时间约束"""
                                eta -= cap[k] * t_ba
                                """"""
                                cap[k] = 0
                                """ser"""
                                delta = eta // t_ba
                                if d[k][1] == 0:
                                    serT += (a[k][i] - dt[k][hs]) * delta
                                else:
                                    serT += (a[k][i] - dt[k][1]) * delta
                                serT += onR[i][j] * delta
                                serT += e[j] * delta
                            else:  # 两都不够
                                t_p = eta // t_ba
                                s_p = d[i][j] - cap[k]
                                l_p = max(t_p, s_p, cap[k])
                                if l_p == cap[k]:
                                    if t_p >= s_p:
                                        cap[k] -= t_p
                                        value[i][j] += t_p
                                        d[i][j] -= t_p
                                        """剩余时间约束"""
                                        eta -= t_p * t_ba
                                        """ser"""
                                        delta = t_p
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta
                                    else:
                                        cap[k] -= s_p
                                        value[i][j] += s_p
                                        d[i][j] -= s_p
                                        """剩余时间约束"""
                                        eta -= s_p * t_ba
                                        """ser"""
                                        delta = s_p
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta
                                elif l_p == t_p:
                                    if cap[k] >= s_p >= 0:
                                        cap[k] -= s_p
                                        value[i][j] += s_p
                                        d[i][j] -= s_p
                                        eta -= s_p * t_ba
                                        """ser"""
                                        delta = s_p
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta
                                    elif 0 <= s_p >= cap[k]:
                                        d[i][j] -= cap[k]
                                        value[i][j] = cap[k]
                                        eta -= cap[k] * t_ba
                                        cap[k] = 0
                                        """ser"""
                                        delta = cap[k]
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta
                                elif l_p == s_p:
                                    if cap[k] >= t_p:
                                        cap[k] -= t_p
                                        value[i][j] += t_p
                                        d[i][j] -= t_p
                                        eta -= t_p * t_ba
                                        """ser"""
                                        delta = t_p
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta
                                    elif cap[k] < t_p:
                                        d[i][j] -= cap[k]
                                        value[i][j] += cap[k]
                                        eta -= cap[k] * t_ba
                                        cap[k] = 0
                                        """ser"""
                                        delta = cap[k]
                                        if d[k][1] == 0:
                                            serT += (a[k][i] - dt[k][hs]) * delta
                                        else:
                                            serT += (a[k][i] - dt[k][1]) * delta
                                        serT += onR[i][j] * delta
                                        serT += e[j] * delta

            t += 1

        print("here")
        self.hs = hs  # hs = self.hs
        self.people = people  # people = self.people
        self.cap = cap  # cap = self.cap
        self.dt = dt  # d = self.d
        self.a = a  # a = self.a
        self.service = serT
        return True

    def __str__(self):
        return "m:{}\nK:{}\nhs:{}\nhe:{}\ncompany:{}\nservice:{}\n".format(self.m, self.K, self.hs, self.he,
                                                                           self.company, self.service)

    def __hash__(self):
        return hash("".join(map(str, str(self.m) + str(self.K) + str(self.hs) + str(self.he) + str(self.z) + str(
            self.h) + str(self.e))))

    def __eq__(self, other):
        if self.m == other.m and self.K == other.K and self.hs == other.hs and self.he == other.he and self.h == other.h and (
                self.e == other.e).all():
            return True
        else:
            return False

    def copy(self):
        indiv = Gene(self.m, self.f, self.hs, self.he, self.z, self.h, self.e)
        indiv.company = copy.deepcopy(self.company)
        indiv.service = copy.deepcopy(self.service)
        indiv.yes = copy.deepcopy(self.yes)
        return indiv

    def pro_Dij(self):
        Dij = np.zeros((31, 31), dtype=np.int_)
        for i, j in range(0, len(od)):
            Dij[i][j] = od[i][j] / 3600

    def pro_Tau(self, k):
        sum = 0
        for m in range(0, len(se)):
            for n in range(0, len(se)):
                sum += self.z[k][m][n]
        return sum


def read():
    """
    :return: se:可做始终站站号；
    pa_tr:断面客流开行数量
    """
    wb = xlrd.open_workbook('../data/附件1：车站数据.xlsx')
    sheet_st = wb.sheet_by_index(0)
    wb = xlrd.open_workbook('../data/附件2：区间运行时间.xlsx')
    sheet_dur = wb.sheet_by_index(0)
    wb = xlrd.open_workbook('../data/附件3：OD客流数据.xlsx')
    sheet_od = wb.sheet_by_index(0)
    wb = xlrd.open_workbook('../data/附件4：断面客流数据.xlsx')
    sheet_pa = wb.sheet_by_index(0)
    wb = xlrd.open_workbook('../data/附件5：其他数据.xlsx')
    sheet_data = wb.sheet_by_index(0)

    capacity = int(sheet_data.col_values(1)[3])
    # print(capacity)
    launch_str = sheet_data.col_values(1)[0]
    launch_min = launch_str[1:5]
    launch_max = launch_str[5:-1]
    launch = (launch_min, launch_max)
    catch_time = int(sheet_data.col_values(1)[1])
    colpa = sheet_pa.col_values(1)
    pa_tr = np.zeros((32, 32), dtype=np.int_)
    for i in range(1, len(colpa)):
        r_val = colpa[i]
        pa_tr[i][i + 1] = r_val

    bool_station = sheet_st.col_values(1)
    se = []
    for i in range(1, len(bool_station)):
        bs = bool_station[i]
        if bs == "是":
            se.append(i)
    od = np.zeros((31, 31), dtype=np.int_)
    for i in range(1, 31):
        for j in range(i, 31):
            # print(i,j)
            od[i][j] = sheet_od.cell_value(i, j)
    """R , L"""
    oR = np.zeros((31, 31), dtype=np.int_)
    oL = np.zeros((31, 31), dtype=np.float_)
    for i in range(1, 30):
        oR[i][i + 1] = sheet_dur.cell_value(i, 1)
        oL[i][i + 1] = sheet_dur.cell_value(i, 2)

    return se, pa_tr, od, oR, oL


def generate():
    while True:
        m = np.random.randint(1, 30)  # n:1
        K = m * np.random.randint(1, 30)  # 总列次
        i = np.random.randint(1, len(se) - 2)
        j = np.random.randint(i + 1, len(se) - 1)
        m = 1
        K = 26
        if 3 < se[j] - se[i] + 1 <= 24:
            # hs = se[i]
            # he = se[j]
            hs = 5
            he = 26
            """z"""
            z = np.zeros((K + 1, 31, 31))
            for k in range(1, K + 1):
                if k % (m + 1) == 0:
                    z[k][1][30] = 1
                else:
                    z[k][hs][he] = 0
            """e"""
            e = np.zeros(31)
            h = random.randint(120, 360)
            for cnt in range(1, 31):
                e[cnt] = random.randint(20, 120)
            gene = Gene(m, K, hs, he, z, h, e)
            # gene = Gene(1, 26, 5, 26, z, h, e)
            # print("here")
            gene.constraint()
            if gene.yes:
                # print("here")
                return gene


def initpop(pop_size):
    pop = []
    for i in range(0, pop_size):
        pop.append(generate())
    return pop


def crossover(pop, pc):
    pop_size = len(pop)
    for i in range(0, pop_size - 1):
        if np.random.random() < pc:
            p = pop[i]
            q = pop[i + 1]
            loc = np.random.randint(2, 30)
            e1 = list(p.e)[0:loc] + list(q.e)[loc:]
            newp = Gene(p.m, p.K, p.hs, p.he, p.z, p.h, np.array(e1))
            newp.constraint()
            if newp.yes:
                pop[i] = newp

            e2 = list(q.e)[0:loc] + list(p.e)[loc:]
            newq = Gene(q.m, q.K, q.hs, q.he, q.z, q.h, np.array(e2))
            newq.constraint()
            if newq.yes:
                pop[i + 1] = newq
    return pop


def mutation(pop, pm):
    pop_size = len(pop)
    for i in range(0, pop_size):
        if np.random.random() < pm:
            qe = pop[i].e
            loc = np.random.randint(1, 31)
            qe[loc] = random.randint(20, 120)
            gene = generate()
            gene.e = qe
            gene.constraint()
            if gene.yes:
                pop[i] = gene
    return pop


def binary_championship_selection(tmp_pop):
    """
        二元锦标赛排序
        :param tmp_pop:按照快速非支配排序排好的中间种群
        :return:被选中的种群
        """
    size = len(tmp_pop)
    # print("====================================")
    # print("size:",size)
    pop = []
    count = 0
    for i in range(0, size):
        first = np.random.randint(0, size)
        sec = np.random.randint(0, size)
        chosen = first if first <= sec else sec
        # print("chosen==========================", end="")
        # print(tmp_pop[chosen].duration)
        pop.append(tmp_pop[chosen])
        count += 1
        if count == size / 2:
            break
    # print(len(pop))
    # sys.exit(666)
    return pop


def run():
    pop_size = 30
    pc = 0.9
    pm = 0.1
    maxGen = 30
    maxGen = 1
    global od, R, L, se, pa, onR
    se, pa, od, R, L = read()
    onR = np.zeros((31, 31))
    for i in range(1, 30):
        for j in range(i + 1, 31):
            for n in range(i, j):
                onR[i][j] += R[n][n + 1]
    # print(onR[2][4])
    # print(pa)
    print(se)
    # print(L)
    # print(od[3][5])
    pop = initpop(pop_size)
    gen = 0
    while gen < maxGen:
        gen = gen + 1
        print("gen: ", gen)
        ancestor = pop.copy()
        for gene in pop:
            gene.fit()
        pop = crossover(pop, pc)
        pop = mutation(pop, pm)
        for gene in pop:
            gene.fit()
        tmp = pop + ancestor
        tmp, paretoRate, paretoNum = NSGA2(tmp)
        pop = binary_championship_selection(tmp)
        """draw"""
        x = [gen] * len(pop)
        y1 = [a.company for a in pop]
        y2 = [a.service for a in pop]
        plt.subplot(3, 1, 1)
        plt.xlabel("gen")
        plt.ylabel("company")
        plt.scatter([gen] * len(y1), y1)
        # 成本散点
        plt.subplot(3, 1, 2)
        plt.xlabel("gen")
        plt.ylabel("service")
        plt.scatter([gen] * len(y2), y2)
        # 亲子合并中间种群帕累托非支配比例
        plt.subplot(3, 1, 3)
        plt.xlabel("gen")
        plt.ylabel("paretoRate")
        plt.scatter(gen, paretoRate, marker='*')
        plt.grid(True, alpha=0.5, axis='both', linestyle=':')
        """"""
        tmp_res = set(pop)  # 按属性去重，实现ep和hash方法
        global result
        result = result | tmp_res
    plt.savefig("../analysis/res2.png")
    """对最终解集进行排序"""
    result = list(result)
    result, res_paretoRate, paretoNum = NSGA2(result)
    result = result[0:paretoNum]
    for indiv in result:
        print(indiv)
        print("e:\n", indiv.e)
        print("发车时间表:\n", indiv.dt)
        print("到达时间表:\n", indiv.a)
    plt.figure()
    x = [a.company for a in result]
    y = [a.service for a in result]
    plt.title("result")
    plt.xlabel("company")
    plt.ylabel("service")
    # plt.annotate(txt, (a.resources[0] for a in result))
    plt.scatter(x, y)
    plt.savefig("../analysis/solution2.png")


if __name__ == '__main__':
    run()
    plt.show()
